<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>B站小剧场视频数据分析</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <style>
        body {
            font-family: "Microsoft YaHei", sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
        }
        .header {
            text-align: center;
            margin-bottom: 20px;
            padding: 15px;
            background: linear-gradient(135deg, #00b4db, #0083b0);
            color: white;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .header h1 {
            margin: 0;
            font-size: 28px;
        }
        .header p {
            margin: 5px 0 0;
            opacity: 0.9;
        }
        .dashboard {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            margin-bottom: 20px;
        }
        @media (max-width: 768px) {
            .dashboard {
                grid-template-columns: 1fr;
            }
        }
        .card {
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
            padding: 15px;
        }
        .chart-container {
            height: 400px;
        }
        .stats-container {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 15px;
        }
        .stat-card {
            background: white;
            border-radius: 8px;
            padding: 15px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
            text-align: center;
        }
        .stat-card h3 {
            margin: 0 0 10px;
            color: #666;
            font-size: 14px;
        }
        .stat-card .value {
            font-size: 24px;
            font-weight: bold;
            color: #0083b0;
        }
        .stat-card .subtext {
            font-size: 12px;
            color: #999;
            margin-top: 5px;
        }
        .data-table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        .data-table th, .data-table td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #eee;
        }
        .data-table th {
            background-color: #f8f9fa;
            font-weight: 600;
        }
        .data-table tr:hover {
            background-color: #f5f5f5;
        }
        .video-link {
            color: #00a1d6;
            text-decoration: none;
        }
        .video-link:hover {
            text-decoration: underline;
        }
        .loading {
            text-align: center;
            padding: 50px;
            font-size: 18px;
            color: #666;
        }
        .tab-controls {
            display: flex;
            margin-bottom: 15px;
            border-bottom: 1px solid #eee;
        }
        .tab-btn {
            padding: 10px 20px;
            background: none;
            border: none;
            border-bottom: 3px solid transparent;
            cursor: pointer;
            font-size: 15px;
            color: #666;
            transition: all 0.3s;
        }
        .tab-btn.active {
            color: #00a1d6;
            border-bottom-color: #00a1d6;
            font-weight: bold;
        }
        .tab-content {
            display: none;
        }
        .tab-content.active {
            display: block;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>B站小剧场视频数据分析</h1>
            <p>基于爬取的150个小剧场视频互动数据 | 最后更新时间: <span id="update-time"></span></p>
        </div>

        <div class="dashboard">
            <div class="card">
                <h2>核心指标分布</h2>
                <div id="main-chart" class="chart-container"></div>
            </div>

            <div class="card">
                <h2>互动率对比</h2>
                <div id="ratio-chart" class="chart-container"></div>
            </div>
        </div>

        <div class="stats-container">
            <div class="stat-card">
                <h3>平均播放量</h3>
                <div class="value" id="avg-views">0</div>
                <div class="subtext">所有视频</div>
            </div>
            <div class="stat-card">
                <h3>最高点赞率</h3>
                <div class="value" id="max-like-rate">0%</div>
                <div class="subtext" id="max-like-title">-</div>
            </div>
            <div class="stat-card">
                <h3>平均投币率</h3>
                <div class="value" id="avg-coin-rate">0%</div>
                <div class="subtext">播放量≥1万的视频</div>
            </div>
            <div class="stat-card">
                <h3>最高收藏率</h3>
                <div class="value" id="max-fav-rate">0%</div>
                <div class="subtext" id="max-fav-title">-</div>
            </div>
        </div>

        <div class="card" style="margin-top: 20px;">
            <div class="tab-controls">
                <button class="tab-btn active" data-tab="top-videos">热门视频</button>
                <button class="tab-btn" data-tab="all-data">完整数据</button>
                <button class="tab-btn" data-tab="about">关于</button>
            </div>

            <div id="top-videos" class="tab-content active">
                <table class="data-table" aria-label="B站小剧场热门视频TOP15数据">
                    <caption class="sr-only">B站小剧场热门视频TOP15（按综合互动率排序）</caption>
                    <thead>
                        <tr>
                             <th scope="col">排名</th>
                             <th scope="col">视频标题</th>
                             <th scope="col">播放量</th>
                             <th scope="col">点赞率</th>
                             <th scope="col">投币率</th>
                             <th scope="col">收藏率</th>
                        </tr>
                    </thead>
                    <tbody id="top-videos-body"></tbody>
                </table>
            </div>

            <div id="all-data" class="tab-content">
                 <table class="data-table" aria-label="B站小剧场完整视频数据集">
                    <caption class="sr-only">B站小剧场全部视频原始数据（包含播放量、点赞、投币、收藏、弹幕和转发等完整指标）</caption>
                    <thead>
                        <tr>
                            <th scope="col">视频标题</th>
                            <th scope="col">播放量</th>
                            <th scope="col">点赞</th>
                            <th scope="col">投币</th>
                            <th scope="col">收藏</th>
                            <th scope="col">弹幕</th>
                            <th scope="col">转发</th>
                        </tr>
                    </thead>
                    <tbody id="all-data-body"></tbody>
                </table>
            </div>

            <div id="about" class="tab-content">
                <h3>关于此数据看板</h3>
                <p>本看板展示通过Scrapy爬取的B站小剧场视频数据，主要分析视频的互动指标：</p>
                <ul>
                    <li><strong>点赞率</strong> = 点赞数 / 播放量 × 100%</li>
                    <li><strong>投币率</strong> = 投币数 / 播放量 × 100%</li>
                    <li><strong>收藏率</strong> = 收藏数 / 播放量 × 100%</li>
                </ul>
                <p>数据来源: B站API | 开发工具: Scrapy + ECharts</p>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 初始化图表
            const mainChart = echarts.init(document.getElementById('main-chart'));
            const ratioChart = echarts.init(document.getElementById('ratio-chart'));

            // 设置加载状态
            document.getElementById('top-videos-body').innerHTML = '<tr><td colspan="6" class="loading">正在加载数据...</td></tr>';
            document.getElementById('all-data-body').innerHTML = '<tr><td colspan="7" class="loading">正在加载数据...</td></tr>';

            // 获取数据
            fetch('../static/shortplay.json')
                .then(response => {
                    if (!response.ok) throw new Error('网络响应异常');
                    return response.json();
                })
                .then(rawData => {
                    // 设置更新时间
                    document.getElementById('update-time').textContent = new Date().toLocaleString();

                    // 处理数据
                    const processedData = processData(rawData);

                    // 渲染图表
                    renderMainChart(processedData);
                    renderRatioChart(processedData);

                    // 更新统计信息
                    updateStats(processedData);

                    // 渲染表格
                    renderTopVideosTable(processedData);
                    renderAllDataTable(processedData);
                })
                .catch(error => {
                    console.error('数据加载失败:', error);
                    document.getElementById('top-videos-body').innerHTML = '<tr><td colspan="6" style="color:red;">数据加载失败: ' + error.message + '</td></tr>';
                    document.getElementById('all-data-body').innerHTML = '<tr><td colspan="7" style="color:red;">数据加载失败: ' + error.message + '</td></tr>';
                });

            // 标签切换
            document.querySelectorAll('.tab-btn').forEach(btn => {
                btn.addEventListener('click', function() {
                    document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
                    document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
                    this.classList.add('active');
                    document.getElementById(this.dataset.tab).classList.add('active');
                });
            });

            // 窗口大小变化时重绘图表
            window.addEventListener('resize', function() {
                mainChart.resize();
                ratioChart.resize();
            });

            // 数据处理函数
            function processData(rawData) {
                return rawData.map(item => {
                    const views = Math.max(1, parseInt(item.views));
                    const likes = parseInt(item.likes);
                    const coins = parseInt(item.coins);
                    const stars = parseInt(item.stars);
                    const shares = parseInt(item.shares);
                    const danmu = parseInt(item.bullet_comments);

                    return {
                        title: item.title,
                        bvid: item.bvid || '',
                        views: views,
                        likes: likes,
                        coins: coins,
                        stars: stars,
                        shares: shares,
                        danmu: danmu,
                        likeRate: (likes / views * 100).toFixed(2),
                        coinRate: (coins / views * 100).toFixed(2),
                        starRate: (stars / views * 100).toFixed(2),
                        shareRate: (shares / views * 100).toFixed(2)
                    };
                });
            }

            // 主图表渲染
            function renderMainChart(data) {
                // 按播放量排序取前20
                const sortedByViews = [...data].sort((a, b) => b.views - a.views).slice(0, 20);

                const option = {
                    title: {
                        text: '播放量TOP20视频互动数据',
                        left: 'center'
                    },
                    tooltip: {
                        trigger: 'axis',
                        axisPointer: {
                            type: 'shadow'
                        },
                        formatter: function(params) {
                            const item = sortedByViews[params[0].dataIndex];
                            return `
                                <div style="font-weight:bold;margin-bottom:5px">${item.title}</div>
                                <div>播放量: ${item.views.toLocaleString()}</div>
                                <div>点赞: ${item.likes.toLocaleString()} (${item.likeRate}%)</div>
                                <div>投币: ${item.coins.toLocaleString()} (${item.coinRate}%)</div>
                                <div>收藏: ${item.stars.toLocaleString()} (${item.starRate}%)</div>
                                <div>弹幕: ${item.danmu.toLocaleString()}</div>
                            `;
                        }
                    },
                    legend: {
                        data: ['播放量', '点赞数', '投币数', '收藏数'],
                        bottom: 10
                    },
                    xAxis: {
                        type: 'category',
                        data: sortedByViews.map(item => item.title.length > 10 ? item.title.substring(0, 8) + '...' : item.title),
                        axisLabel: {
                            rotate: 45,
                            interval: 0
                        }
                    },
                    yAxis: [
                        {
                            type: 'value',
                            name: '数量',
                            axisLabel: {
                                formatter: '{value}'
                            }
                        },
                        {
                            type: 'value',
                            name: '比率(%)',
                            min: 0,
                            max: 30,
                            axisLabel: {
                                formatter: '{value}%'
                            }
                        }
                    ],
                    series: [
                        {
                            name: '播放量',
                            type: 'bar',
                            barGap: 0,
                            data: sortedByViews.map(item => item.views),
                            itemStyle: {
                                color: '#5470C6'
                            }
                        },
                        {
                            name: '点赞数',
                            type: 'bar',
                            data: sortedByViews.map(item => item.likes),
                            itemStyle: {
                                color: '#91CC75'
                            }
                        },
                        {
                            name: '投币数',
                            type: 'bar',
                            data: sortedByViews.map(item => item.coins),
                            itemStyle: {
                                color: '#FAC858'
                            }
                        },
                        {
                            name: '收藏数',
                            type: 'bar',
                            data: sortedByViews.map(item => item.stars),
                            itemStyle: {
                                color: '#EE6666'
                            }
                        },
                        {
                            name: '点赞率',
                            type: 'line',
                            yAxisIndex: 1,
                            data: sortedByViews.map(item => parseFloat(item.likeRate)),
                            itemStyle: {
                                color: '#73C0DE'
                            },
                            symbolSize: 8,
                            lineStyle: {
                                width: 3
                            }
                        }
                    ],
                    dataZoom: [
                        {
                            type: 'slider',
                            start: 0,
                            end: 100,
                            bottom: '5%'
                        }
                    ]
                };

                mainChart.setOption(option);
            }

            // 比率图表渲染
            function renderRatioChart(data) {
                // 按播放量筛选(>1000)并按点赞率排序取前15
                const filteredData = data.filter(item => item.views > 1000);
                const sortedByLikeRate = [...filteredData].sort((a, b) => b.likeRate - a.likeRate).slice(0, 15);

                const option = {
                    title: {
                        text: '高互动率视频(播放量>1000)',
                        left: 'center'
                    },
                    tooltip: {
                        trigger: 'axis',
                        axisPointer: {
                            type: 'shadow'
                        }
                    },
                    legend: {
                        data: ['点赞率', '投币率', '收藏率'],
                        bottom: 10
                    },
                    xAxis: {
                        type: 'category',
                        data: sortedByLikeRate.map(item => item.title.length > 8 ? item.title.substring(0, 6) + '...' : item.title),
                        axisLabel: {
                            rotate: 45,
                            interval: 0
                        }
                    },
                    yAxis: {
                        type: 'value',
                        name: '比率(%)',
                        axisLabel: {
                            formatter: '{value}%'
                        }
                    },
                    series: [
                        {
                            name: '点赞率',
                            type: 'bar',
                            data: sortedByLikeRate.map(item => parseFloat(item.likeRate)),
                            itemStyle: {
                                color: '#EE6666'
                            },
                            label: {
                                show: true,
                                position: 'top',
                                formatter: '{c}%'
                            }
                        },
                        {
                            name: '投币率',
                            type: 'bar',
                            data: sortedByLikeRate.map(item => parseFloat(item.coinRate)),
                            itemStyle: {
                                color: '#FAC858'
                            },
                            label: {
                                show: true,
                                position: 'top',
                                formatter: '{c}%'
                            }
                        },
                        {
                            name: '收藏率',
                            type: 'bar',
                            data: sortedByLikeRate.map(item => parseFloat(item.starRate)),
                            itemStyle: {
                                color: '#91CC75'
                            },
                            label: {
                                show: true,
                                position: 'top',
                                formatter: '{c}%'
                            }
                        }
                    ],
                    dataZoom: [
                        {
                            type: 'slider',
                            start: 0,
                            end: 100,
                            bottom: '5%'
                        }
                    ]
                };

                ratioChart.setOption(option);
            }

            // 更新统计信息
            function updateStats(data) {
                // 平均播放量
                const avgViews = Math.round(data.reduce((sum, item) => sum + item.views, 0) / data.length);
                document.getElementById('avg-views').textContent = avgViews.toLocaleString();

                // 最高点赞率
                const maxLike = data.reduce((max, item) =>
                    parseFloat(item.likeRate) > parseFloat(max.likeRate) ? item : max
                );
                document.getElementById('max-like-rate').textContent = maxLike.likeRate + '%';
                document.getElementById('max-like-title').textContent = maxLike.title.length > 15 ?
                    maxLike.title.substring(0, 12) + '...' : maxLike.title;

                // 平均投币率(播放量≥1万)
                const highViewItems = data.filter(item => item.views >= 10000);
                const avgCoinRate = highViewItems.length > 0 ?
                    (highViewItems.reduce((sum, item) => sum + parseFloat(item.coinRate), 0) / highViewItems.length).toFixed(2) : 0;
                document.getElementById('avg-coin-rate').textContent = avgCoinRate + '%';

                // 最高收藏率
                const maxFav = data.reduce((max, item) =>
                    parseFloat(item.starRate) > parseFloat(max.starRate) ? item : max
                );
                document.getElementById('max-fav-rate').textContent = maxFav.starRate + '%';
                document.getElementById('max-fav-title').textContent = maxFav.title.length > 15 ?
                    maxFav.title.substring(0, 12) + '...' : maxFav.title;
            }

            // 渲染热门视频表格
            function renderTopVideosTable(data) {
                // 按综合评分排序 (点赞率*0.5 + 投币率*0.3 + 收藏率*0.2)
                const sorted = [...data].sort((a, b) =>
                    (parseFloat(b.likeRate)*0.5 + parseFloat(b.coinRate)*0.3 + parseFloat(b.starRate)*0.2) -
                    (parseFloat(a.likeRate)*0.5 + parseFloat(a.coinRate)*0.3 + parseFloat(a.starRate)*0.2)
                ).slice(0, 15);

                const tbody = document.getElementById('top-videos-body');
                tbody.innerHTML = '';

                sorted.forEach((item, index) => {
                    const row = document.createElement('tr');

                    row.innerHTML = `
                        <td>${index + 1}</td>
                        <td title="${item.title}">
                            ${item.title.length > 20 ? item.title.substring(0, 18) + '...' : item.title}
                        </td>
                        <td>${item.views.toLocaleString()}</td>
                        <td>${item.likeRate}%</td>
                        <td>${item.coinRate}%</td>
                        <td>${item.starRate}%</td>
                    `;

                    tbody.appendChild(row);
                });
            }

            // 渲染完整数据表格
            function renderAllDataTable(data) {
                const tbody = document.getElementById('all-data-body');
                tbody.innerHTML = '';

                data.forEach(item => {
                    const row = document.createElement('tr');

                    row.innerHTML = `
                        <td title="${item.title}">
                                ${item.title.length > 30 ? item.title.substring(0, 28) + '...' : item.title}
                        </td>
                        <td>${item.views.toLocaleString()}</td>
                        <td>${item.likes.toLocaleString()}</td>
                        <td>${item.coins.toLocaleString()}</td>
                        <td>${item.stars.toLocaleString()}</td>
                        <td>${item.danmu.toLocaleString()}</td>
                        <td>${item.shares.toLocaleString()}</td>
                    `;

                    tbody.appendChild(row);
                });
            }
        });
    </script>
</body>
</html>